#ifndef GFXD3D9_H
#define GFXD3D9_H

#ifdef _DEBUG
#define D3D_DEBUG_INFO
#endif

#include <d3d9.h>
#include <d3dx9.h>

#include <imGraphicsInterfaces.h>
#include <Config.h>

#define DECLDIR __declspec(dllexport)

namespace imGraphics {

	// DLL implementation functions
	extern "C" __declspec(dllexport) HRESULT CreateRenderDevice(HINSTANCE hDLL, iRenderer **pInterface);
	extern "C" __declspec(dllexport) HRESULT ReleaseRenderDevice(iRenderer **pInterface);

	// Global variables
	extern LPDIRECT3DDEVICE9 g_DeviceD3D9;
	extern D3DVERTEXELEMENT9 declVertex[4];
	extern LPDIRECT3DVERTEXDECLARATION9 VertexDecl;

	extern D3DXMATRIX *g_World, *g_View, *g_Projection, *g_VP;

	//
	// Shader
	//
	class DECLDIR D3D9Shader : public iShader {
	private:
		LPD3DXEFFECT m_Fx;
	public:
		D3D9Shader();
		~D3D9Shader();

		void LoadShader(char* Path);

		void SetDiffuse(const Vector4 &Diffuse);
		void SetSpecular(const Vector4 &Specular);

		void SetSrcBlend(const imGraphics::BlendAlpha &SrcBlend);
		void SetDestBlend(const imGraphics::BlendAlpha &DestBlend);

		void SetWireframe(const bool &Wireframe);
		void SetCullMode(const CullMode &Mode);

		void FillMatrices();
		void StartRender();
		void EndRender();
	};

	extern LPDIRECT3DTEXTURE9 g_Tex[NUMTEXTURES];

	//
	// Texture
	//
	class DECLDIR D3D9Texture : public iTexture {
	private:
		LPDIRECT3DTEXTURE9 m_Tex;
	public:
		D3D9Texture();
		~D3D9Texture();

		bool ReopenFile();

		void LoadFromFile(std::string &Path);
		void Release();
		void FillSolid(Vector4 &Col);
		
		void UseTexture(int Channel);
	};

	//
	// Mesh Driver
	//
	class DECLDIR D3D9MeshDriver : public iMeshDriver {
	private:
		VOID* Vertices;
		VOID* Indices;
		LPDIRECT3DVERTEXBUFFER9 VertexBuffer;
		LPDIRECT3DINDEXBUFFER9 IndexBuffer;
		unsigned int NumVerts, NumFaces, NumIndices;
		unsigned int IndexSize;
	public:
		D3D9MeshDriver(void);
		~D3D9MeshDriver(void);

		void Clear(void);

		void Hook(Vertex* _Verts, unsigned int _NumVerts, void* _Indices, unsigned int _NumIndices, unsigned int _IndexSize);
		void Predraw(void);
		void Draw(int Subset);
	};

	//
	// Renderer
	//
	class DECLDIR D3D9Renderer : public iRenderer {
		unsigned int m_w, m_h;
		unsigned int m_DisplayInterval;
		DisplayMode m_Disp;

		float m_Aspect, m_NearZ, m_FarZ, m_FOV;

		HWND m_WndHandle;
		HINSTANCE m_hInstance;

		Matrix m_World, m_View, m_Projection;
		D3DXMATRIX m_D3DWorld, m_D3DView, m_D3DProjection, m_D3DVP;

		LPDIRECT3D9 pD3D;
		LPDIRECT3DDEVICE9 pd3dDevice;
	public:
		D3D9Renderer();
		~D3D9Renderer();

		void Init (HINSTANCE, HWND);
		void Init ();
		void Release ();
		void Cleanup ();

		void SetupFromFile(Config* Cfg);

		void SetRes		(unsigned int w, unsigned int h);
		void SetMode	(DisplayMode Mode);
		void SetVSync	(bool vSync);
		
		void SetWorldMatrix			(Matrix &NewWorld);
		void SetViewMatrix			(Matrix &NewView);
		void SetProjectionMatrix	(Matrix &NewProjection);

		iMeshDriver*	GetMeshDriver ();
		iRenderTarget*	GetNewRenderTarget ();
		iShader*		GetNewShader ();
		iTexture*		GetNewTexture ();

		void StartFrame ();
		void EndFrame ();

		void CheckAllFiles() { iFile::CheckAllFiles(); }
		void CollectGarbage() { iMMObj::CollectGarbage(); }
	};
}

#endif